package com.tevintao.marketingsystem.xposted;

import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.hardware.usb.UsbDevice;
import android.net.DhcpInfo;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings;
import android.telephony.CellIdentityCdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellLocation;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
import android.view.WindowManager;

import com.tevintao.marketingsystem.activity.ActSetGps;
import com.tevintao.marketingsystem.log.LogManager;
import com.tevintao.marketingsystem.services.AccountManager;
import com.tevintao.marketingsystem.services.MockServices;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import de.robv.android.xposed.XC_MethodHook;

/**
 * Build.BOARD // 主板  
 * Build.BRAND // android系统定制商  
 * Build.CPU_ABI // cpu指令集  
 * Build.DEVICE // 设备参数  
 * Build.DISPLAY // 显示屏参数  
 * Build.FINGERPRINT // 硬件名称  
 * Build.HOST  
 * Build.ID // 修订版本列表  
 * Build.MANUFACTURER // 硬件制造商  
 * Build.MODEL // 版本  
 * Build.PRODUCT // 手机制造商  
 * Build.TAGS // 描述build的标签  
 * Build.TIME  
 * Build.TYPE // builder类型  
 * Build.USER
 * <p/>
 * Build.ID=KTU84P
 * Build.DISPLAY=TBE570711_9721_V050107
 * Build.PRODUCT=msm8610
 * Build.DEVICE=msm8610
 * Build.BOARD=MSM8610
 * Build.CPU_ABI=armeabi-v7a
 * Build.CPU_ABI2=armeabi
 * Build.MANUFACTURER=K-Touch
 * Build.BRAND=qcom
 * Build.MODEL=K-Touch E8
 * Build.BOOTLOADER=unknown
 * Build.RADIO=unknown
 * Build.HARDWARE=qcom
 * Build.SERIAL=E8
 */
public class HookDeviceInfo {

    public static final String TAG = HookDeviceInfo.class.getSimpleName();

    private static HookDeviceInfo ourInstance = new HookDeviceInfo();

    public static HookDeviceInfo getInstance() {
        return ourInstance;
    }

    private HookDeviceInfo() {
    }


    public void hookDeviceInfo() {
        if (AccountManager.CURREN_DEVICE_TYPE == AccountManager.TYPE_NO_DEVICE_INFO) {
            setTelephoneInfoJustGsm();
        } else {
            if (AccountManager.CURREN_DEVICE_TYPE == AccountManager.TYPE_DEVICE_LENOVO) {
                hookSystemPropertiesLenovo();
            } else if (AccountManager.CURREN_DEVICE_TYPE == AccountManager.TYPE_DEVICE_SANXIN) {
                hookSystemProperties();
            }

//        hookFiled(Build.class, null, "CPU_ABI", "I MA TTW");
//        hookFiled(Build.class, null, "CPU_ABI2", "");
//        setHookField(Build.class, null, "SERIAL", "");
//        setHookField(Build.class, null, "DISPLAY", "");
//        setHookField(Build.class, null, "MODEL", "");


//        Build.ID=KTU84P 大写的字母加数字，5-8位 或者 使用手机型号信息device_name
            hookFiled(Build.class, null, "ID", "Build.ID");
//        Build.DISPLAY=TBE570711_9721_V050107 大写的字母加数字，5-8位 或者 使用手机型号信息device
            hookFiled(Build.class, null, "DISPLAY", "Build.DISPLAY");
//        Build.PRODUCT=msm8610 使用手机型号信息device_product
            hookFiled(Build.class, null, "PRODUCT", "Build.PRODUCT");
//        Build.DEVICE=msm8610 使用手机型号信息device_product
            hookFiled(Build.class, null, "DEVICE", "Build.DEVICE");
//        Build.BOARD=MSM8610 使用手机型号信息device_product
            hookFiled(Build.class, null, "BOARD", "Build.BOARD");
//        Build.MANUFACTURER=K-Touch 使用手机型号信息device_manufacture
            hookFiled(Build.class, null, "MANUFACTURER", "Build.MANUFACTURER");
//        Build.BRAND=qcom 使用手机型号信息device_brand
            hookFiled(Build.class, null, "BRAND", "Build.BRAND");
//        Build.MODEL=K-Touch E8 使用手机型号信息device_name
            hookFiled(Build.class, null, "MODEL", "Build.MODEL");
//        Build.HARDWARE=qcom 使用手机型号信息device_brand
            hookFiled(Build.class, null, "HARDWARE", "Build.HARDWARE");
//        Build.SERIAL=E8  使用手机型号信息device_product
            hookFiled(Build.class, null, "SERIAL", "Build.SERIAL");
//
//        Build.VERSION.CODENAME=REL  直接试用REL
            hookFiled(Build.VERSION.class, null, "CODENAME", "Build.VERSION.CODENAME");
//        Build.VERSION.INCREMENTAL=TBE570711_9721_V050107 和Build.DISPLAY相同
            hookFiled(Build.VERSION.class, null, "INCREMENTAL", "Build.VERSION.INCREMENTAL");

//        Build.VERSION.RELEASE=4.4.4 使用手机型号信息os_version
            hookFiled(Build.VERSION.class, null, "RELEASE", "Build.VERSION.RELEASE");
//        Build.VERSION.SDK=19 使用手机型号信息os_version对应信息
//            hookFiled(Build.VERSION.class, null, "SDK", "Build.VERSION.SDK");
//        Build.VERSION.SDK_INT=19 使用手机型号信息os_version对应信息
//            hookIntFiled(Build.VERSION.class, null, "SDK_INT", "Build.VERSION.SDK_INT");

//        hookFiled(Build.class, null, "CPU_ABI", "I MA TTW");
            setHookField(Build.class, null, "FINGERPRINT", "");

            hookWifiManagerMethod();
            setSettingId();

            setTelephoneInfo();
            hookBluetooth();
            hookUserDer();
            hookInputDevice();
            hookNetworkInfoName();

//        hookDisplay();
        }
    }


    //-----Method----
    public boolean hookWifiManagerMethod() {
        HookDeviceModule.hookAllMethodByName(WifiManager.class, "getConnectionInfo", new mMacXcHook("getConnectionInfo"));
        HookDeviceModule.hookAllMethodByName(WifiManager.class, "getDhcpInfo", new mMacXcHook("getDhcpInfo"));
        HookDeviceModule.hookAllMethodByName(WifiManager.class, "getConfiguredNetworks", new mMacXcHook("getConfiguredNetworks"));
        HookDeviceModule.hookAllMethodByName(WifiManager.class, "getScanResults", new mMacXcHook("getScanResults"));
        HookDeviceModule.hookAllMethodByName(WifiManager.class, "getWifiApConfiguration", new mMacXcHook("getWifiApConfiguration"));
        return true;
    }


    private class mMacXcHook extends XC_MethodHook {
        private String mMethodName = null;

        public mMacXcHook(String methodName) {
            this.mMethodName = methodName;
        }

        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//            LogManager.getInstance().logInfo("hookWifiManagerMethod " + mMethodName);
            if ("getConnectionInfo".equals(mMethodName)) {
                LogManager.getInstance().logHookDeviceInfo("afterHookedMethod mMethodName = " + mMethodName);
                if (param.getResult() != null) {
                    WifiInfo result = (WifiInfo) param.getResult();
                    WifiInfo wInfo = WifiInfo.class.getConstructor(WifiInfo.class).newInstance(result);

                    if (!MockServices.checkClient()) {
                        return;
                    }

                    // BSSID
                    try {
                        Field fieldBSSID = WifiInfo.class.getDeclaredField("mBSSID");
                        fieldBSSID.setAccessible(true);
                        fieldBSSID.set(wInfo, WXDeviceManager.getInstance().getDeviceInfo("BSSID"));
                    } catch (Throwable ex) {
                        LogManager.getInstance().logHookDeviceError(mMethodName + "BSSID afterHookedMethod" + ex.getMessage());
                    }

                    // IP address
                    try {
                        Field fieldIp = result.getClass().getDeclaredField("mIpAddress");
                        LogManager.getInstance().logHookDeviceInfo("find mIpAddress" + fieldIp);
                        fieldIp.setAccessible(true);
                        String ip = WXDeviceManager.getInstance().getDeviceInfo("InetAddress");
                        InetAddress address = InetAddress.getByName(ip);
                        fieldIp.set(wInfo, address);
                    } catch (Throwable ex) {
                        LogManager.getInstance().logHookDeviceError(mMethodName + " InetAddress afterHookedMethod " + ex.getMessage());
                    }

                    // MAC address
                    try {
                        Field fieldMAC = WifiInfo.class.getDeclaredField("mMacAddress");
                        fieldMAC.setAccessible(true);
                        fieldMAC.set(wInfo, WXDeviceManager.getInstance().getDeviceInfo("MAC"));
                    } catch (Throwable ex) {
                        LogManager.getInstance().logHookDeviceError(mMethodName + "MAC afterHookedMethod" + ex.getMessage());
                    }

                    // SSID
                    String ssid = WXDeviceManager.getInstance().getDeviceInfo("SSID");
                    if (TextUtils.isEmpty(ssid)) {
                        LogManager.getInstance().logHookDeviceError("ssid = " + ssid);
                        ssid = "";
                    }
                    try {
                        Field fieldSSID = WifiInfo.class.getDeclaredField("mSSID");
                        fieldSSID.setAccessible(true);
                        fieldSSID.set(wInfo, ssid);
                    } catch (Throwable ex) {
                        try {
                            Field fieldWifiSsid = WifiInfo.class.getDeclaredField("mWifiSsid");
                            fieldWifiSsid.setAccessible(true);
                            Object mWifiSsid = fieldWifiSsid.get(wInfo);
                            if (mWifiSsid != null) {
                                // public static WifiSsid
                                // createFromAsciiEncoded(String
                                // asciiEncoded)
                                Method methodCreateFromAsciiEncoded = mWifiSsid.getClass().getDeclaredMethod(
                                        "createFromAsciiEncoded", String.class);
                                fieldWifiSsid.set(wInfo, methodCreateFromAsciiEncoded.invoke(null, ssid));
                            }
                        } catch (Throwable exex) {
                            LogManager.getInstance().logHookDeviceError(mMethodName + "createFromAsciiEncoded afterHookedMethod" + exex.getMessage());
                        }
                    }
                    param.setResult(wInfo);
                }
            } else if ("getDhcpInfo".equals(mMethodName)) {
                if (param.getResult() != null) {
                    DhcpInfo result = (DhcpInfo) param.getResult();
                    DhcpInfo dInfo = DhcpInfo.class.getConstructor(DhcpInfo.class).newInstance(result);
                    dInfo.ipAddress = WXDeviceManager.getInstance().getDeviceInfoInt("IPInt");
                    dInfo.gateway = dInfo.ipAddress;
                    dInfo.dns1 = dInfo.ipAddress;
                    dInfo.dns2 = dInfo.ipAddress;
                    dInfo.serverAddress = dInfo.ipAddress;
                    param.setResult(dInfo);
                }
            } else if ("getConfiguredNetworks".equals(mMethodName)) {
                if (param.getResult() != null)
                    param.setResult(new ArrayList<WifiConfiguration>());
            } else if ("getScanResults".equals(mMethodName)) {
                if (param.getResult() != null) {
//                    for (ScanResult result : (List<ScanResult>) param.getResult()) {
//                        LogManager.getInstance().logError("result = " + result.toString());
//                    }
//                    LogManager.getInstance().logError("getDefaultScanResult = " + getDefaultScanResult((List<ScanResult>) param.getResult()));

                    ScanResult myResult = getDefaultScanResult((List<ScanResult>) param.getResult());
                    if (myResult == null) {
                        LogManager.getInstance().logError("getScanResults myResult is null");
                        param.setResult(new ArrayList<ScanResult>());
                    } else {
                        ArrayList<ScanResult> rList = new ArrayList<>();
                        rList.add(myResult);
                        param.setResult(rList);
                    }
                }
            } else if ("getWifiApConfiguration".equals(mMethodName)) {
                if (param.getResult() != null)
                    param.setResult(null);
            }
        }
    }

    private ScanResult getDefaultScanResult(List<ScanResult> list) {
        if (null != list && list.size() > 0) {
            ScanResult result = list.get(0);
            String bSSID = "";
            String ssid = "";
            ssid = WXDeviceManager.getInstance().getDeviceInfo("SSID");
            bSSID = WXDeviceManager.getInstance().getDeviceInfo("BSSID");
            result.BSSID = bSSID;
            result.SSID = ssid;
            return result;
        }
        return null;
    }

    private void setSettingId() {
        HookDeviceModule.hookAllMethodByName(Settings.Secure.class, "getString", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                String name = (param.args.length > 1 ? (String) param.args[1] : null);
                if (Settings.Secure.ANDROID_ID.equals(name)) {
                    String id = (String) param.getResult();
                    if (id != null) {
                        param.setResult(WXDeviceManager.getInstance().getDeviceInfo("ANDROID_ID"));
                    }
                }
            }
        });
    }

    public void setTelephoneInfoJustGsm() {
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getAllCellInfo", new TelephonyXCHook("getAllCellInfo"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getCellLocation", new TelephonyXCHook("getCellLocation"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getNeighboringCellInfo", new TelephonyXCHook("getNeighboringCellInfo"));
    }

    public void setTelephoneInfo() {

        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getAllCellInfo", new TelephonyXCHook("getAllCellInfo"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getCellLocation", new TelephonyXCHook("getCellLocation"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getNeighboringCellInfo", new TelephonyXCHook("getNeighboringCellInfo"));


        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "listen", new TelephonyXCHook("listen"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getDeviceId", new TelephonyXCHook("getDeviceId"));

        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getLine1AlphaTag", new TelephonyXCHook("PhoneNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getLine1Number", new TelephonyXCHook("PhoneNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getMsisdn", new TelephonyXCHook("PhoneNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getVoiceMailAlphaTag", new TelephonyXCHook("PhoneNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getVoiceMailNumber", new TelephonyXCHook("PhoneNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getCompleteVoiceMailNumber", new TelephonyXCHook("PhoneNumber"));


        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getNetworkCountryIso", new TelephonyXCHook("getNetworkCountryIso"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getNetworkOperator", new TelephonyXCHook("getNetworkOperator"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getNetworkOperatorName", new TelephonyXCHook("getNetworkOperatorName"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getSimCountryIso", new TelephonyXCHook("getSimCountryIso"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getSimOperator", new TelephonyXCHook("getSimOperator"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getSimOperatorName", new TelephonyXCHook("getSimOperatorName"));

        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getSimSerialNumber", new TelephonyXCHook("getSimSerialNumber"));
        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getIccSerialNumber", new TelephonyXCHook("getSimSerialNumber"));

        HookDeviceModule.hookAllMethodByName(TelephonyManager.class, "getSubscriberId", new TelephonyXCHook("getSubscriberId"));
    }

    private class TelephonyXCHook extends XC_MethodHook {
        private String mMethodName = null;

        public TelephonyXCHook(String methodName) {
            this.mMethodName = methodName;
        }

        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//            LogManager.getInstance().logInfo("setTelephoneInfo " + mMethodName);
            if ("listen".equals(mMethodName)) {
                if (param.args.length > 1 && param.args[0] instanceof PhoneStateListener && param.args[1] instanceof Integer) {
                    PhoneStateListener listener = (PhoneStateListener) param.args[0];
                    int event = (Integer) param.args[1];
                    if (event == PhoneStateListener.LISTEN_NONE) {
                        // Remove
                        synchronized (mListener) {
                            XPhoneStateListener xListener = mListener.get(listener);
                            if (xListener != null) {
                                param.args[0] = xListener;
                            }
                        }
                    } else {
                        try {
                            // Replace
                            XPhoneStateListener xListener;
                            synchronized (mListener) {
                                xListener = mListener.get(listener);
                                if (xListener == null) {
                                    xListener = new XPhoneStateListener(listener);
                                    mListener.put(listener, xListener);
                                }
                            }
                            param.args[0] = xListener;
                        } catch (Throwable ignored) {
                            LogManager.getInstance().logHookDeviceInfo("ThelePhoneXCHook listen Replace " + ignored);
                        }
                    }
                }
            }
        }


        private int getLatInt() {
            float lat = WxLocationManager.getInstance().getLocationFloat(ActSetGps.KEY_LAT);
            if (lat > 0) {
                return (int) lat * 14400;
            }
            return -1;
        }


        private int getLonInt() {
            float lon = WxLocationManager.getInstance().getLocationFloat(ActSetGps.KEY_LON);
            if (lon > 0) {
                return (int) lon * 14400;
            }
            return -1;
        }


        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            if ("getAllCellInfo".equals(mMethodName)) {
                ArrayList<CellInfo> returnInfo = new ArrayList<>();
                LogManager.getInstance().logInfo("getAllCellInfo param.getResult() = " + param.getResult());
                if (param.getResult() != null) {
                    List<CellInfo> cellList = (List<CellInfo>) param.getResult();
//                    for (CellInfo info : (List<CellInfo>) param.getResult()) {
//                        LogManager.getInstance().logInfo("info = " + info);
//                    }
                    if (cellList.size() > 0) {
                        LogManager.getInstance().logInfo("cellList.size() > 0 ");
                        CellInfo cellInfo = cellList.get(0);
                        if (cellInfo instanceof CellInfoCdma) {
                            LogManager.getInstance().logInfo("cellInfo instanceof CellInfoCdma");
                            CellInfoCdma cdma = (CellInfoCdma) cellInfo;
                            CellIdentityCdma cellIdentity = cdma.getCellIdentity();

                            try {
                                Field mNetworkId = CellIdentityCdma.class.getDeclaredField("mNetworkId");
                                mNetworkId.setAccessible(true);
//                                mNetworkId.set(cellIdentity, 53);
                                mNetworkId.set(cellIdentity, WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_LAC));

                                Field mSystemId = CellIdentityCdma.class.getDeclaredField("mSystemId");
                                mSystemId.setAccessible(true);
//                                mSystemId.set(cellIdentity, 13938);
                                mSystemId.set(cellIdentity, WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_MNC));

                                Field mBasestationId = CellIdentityCdma.class.getDeclaredField("mBasestationId");
                                mBasestationId.setAccessible(true);
//                                mBasestationId.set(cellIdentity, 31888);
                                mBasestationId.set(cellIdentity, WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_CID));

                                Field mLongitude = CellIdentityCdma.class.getDeclaredField("mLongitude");
                                mLongitude.setAccessible(true);
                                mLongitude.set(cellIdentity, getLonInt());
//                                mLongitude.set(cellIdentity, 1568760);

                                Field mLatitude = CellIdentityCdma.class.getDeclaredField("mLatitude");
                                mLatitude.setAccessible(true);
                                mLatitude.set(cellIdentity, getLatInt());
//                                mLatitude.set(cellIdentity, 494308);
                            } catch (NoSuchFieldException e) {
                                e.printStackTrace();
                                LogManager.getInstance().logError("getAllCellInfo NoSuchFieldException " + e);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (IllegalArgumentException e) {
                                e.printStackTrace();
                                LogManager.getInstance().logError("getAllCellInfo IllegalArgumentException " + e);
                            } catch (Exception e) {
                                e.printStackTrace();
                                LogManager.getInstance().logError("getAllCellInfo Exception " + e);
                            }

                            returnInfo.add(cellInfo);
                        }
                    }
                    LogManager.getInstance().logInfo("getAllCellInfo returnInfo = " + returnInfo);
                }
                param.setResult(returnInfo);
            } else if ("getCellLocation".equals(mMethodName)) {
                if (param.getResult() != null) {
                    CellLocation location = (CellLocation) param.getResult();
                    LogManager.getInstance().logInfo("location = " + location.toString() + " cls = " + location.getClass().getName());
                    if (param.getResult() instanceof GsmCellLocation) {
                        GsmCellLocation gsmCellLocation = (GsmCellLocation) param.getResult();
                        LogManager.getInstance().logInfo("gsmCellLocation = " + gsmCellLocation.toString());
//                        gsmCellLocation.setLacAndCid(getLac(), getCid());
                        gsmCellLocation.setLacAndCid(WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_LAC), WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_CID));

//                        try {
//                            Field mPsc = GsmCellLocation.class.getDeclaredField("mPsc");
//                            mPsc.setAccessible(true);
//                            mPsc.set(gsmCellLocation, 689);
//                        } catch (NoSuchFieldException e) {
//                            e.printStackTrace();
//                            LogManager.getInstance().logError("NoSuchFieldException " + e);
//                        } catch (IllegalAccessException e) {
//                            e.printStackTrace();
//                            LogManager.getInstance().logError("IllegalAccessException " + e);
//                        } catch (IllegalArgumentException e) {
//                            e.printStackTrace();
//                            LogManager.getInstance().logError("IllegalArgumentException " + e);
//                        }catch (Exception e) {
//                            e.printStackTrace();
//                            LogManager.getInstance().logError("Exception " + e);
//                        }

                        param.setResult(gsmCellLocation);
                        LogManager.getInstance().logInfo("getCellLocation GsmCellLocation set hook location  = " + gsmCellLocation.toString());
                    } else if (param.getResult() instanceof CdmaCellLocation) {
                        CdmaCellLocation cdmaCellLocation = (CdmaCellLocation) param.getResult();
                        LogManager.getInstance().logInfo("cdmaCellLocation = " + cdmaCellLocation.toString());
//                        cdmaCellLocation.setStateInvalid();
//                        cdmaCellLocation.setCellLocationData(getCid(), getLatInt(), getLonInt(), getMnc(), getLac());
                        cdmaCellLocation.setCellLocationData(WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_CID), getLatInt(), getLonInt(), WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_MNC), WxLocationManager.getInstance().getLocationInt(ActSetGps.KEY_LAC));
                        param.setResult(cdmaCellLocation);
                        LogManager.getInstance().logInfo("getCellLocation CdmaCellLocation set hook location  = " + cdmaCellLocation.toString());
                    }
                }

            } else if ("getNeighboringCellInfo".equals(mMethodName)) {
                LogManager.getInstance().logInfo("getNeighboringCellInfo param.getResult() = " + param.getResult());
                if (param.getResult() != null) {
                    for (NeighboringCellInfo cellInfo : (List<NeighboringCellInfo>) param.getResult()) {
                        LogManager.getInstance().logInfo("NeighboringCellInfo cellInfo = " + cellInfo);
                    }
                }
                param.setResult(new ArrayList<NeighboringCellInfo>());
            } else {
                String value = "";
                if (MockServices.checkClient()) {
                    value = WXDeviceManager.getInstance().getDeviceInfo(mMethodName);
                }
//                LogManager.getInstance().logInfo(mMethodName + " = " + value);
                if (param.getResult() != null)
                    param.setResult(value);
            }
        }
    }

    private static final Map<PhoneStateListener, XPhoneStateListener> mListener = new WeakHashMap<>();

    private class XPhoneStateListener extends PhoneStateListener {
        private PhoneStateListener mListener;

        public XPhoneStateListener(PhoneStateListener listener) {
            mListener = listener;
        }

        @Override
        public void onCallForwardingIndicatorChanged(boolean cfi) {
            mListener.onCallForwardingIndicatorChanged(cfi);
        }

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            String phoneNum = "";
            if (MockServices.checkClient()) {
                phoneNum = WXDeviceManager.getInstance().getDeviceInfo("PhoneNumber");
            }
            LogManager.getInstance().logHookDeviceInfo("onCallStateChanged = state = " + state + " incomingNumber = " + incomingNumber + " phoneNum = " + phoneNum);
            mListener.onCallStateChanged(state, phoneNum);
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void onCellInfoChanged(List<CellInfo> cellInfo) {
            mListener.onCellInfoChanged(new ArrayList<CellInfo>());
        }

        @Override
        public void onCellLocationChanged(CellLocation location) {
            mListener.onCellLocationChanged(CellLocation.getEmpty());
        }

        @Override
        public void onDataActivity(int direction) {
            mListener.onDataActivity(direction);
        }

        @Override
        public void onDataConnectionStateChanged(int state) {
            mListener.onDataConnectionStateChanged(state);
        }

        @Override
        public void onDataConnectionStateChanged(int state, int networkType) {
            mListener.onDataConnectionStateChanged(state, networkType);
        }

        @Override
        public void onMessageWaitingIndicatorChanged(boolean mwi) {
            mListener.onMessageWaitingIndicatorChanged(mwi);
        }

        @Override
        public void onServiceStateChanged(ServiceState serviceState) {
            mListener.onServiceStateChanged(serviceState);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void onSignalStrengthChanged(int asu) {
            mListener.onSignalStrengthChanged(asu);
        }

        @Override
        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
            mListener.onSignalStrengthsChanged(signalStrength);
        }
    }

    private void hookBluetooth() {
        HookDeviceModule.hookAllMethodByName(BluetoothAdapter.class, "getAddress", xc_mac_methodHook);
        HookDeviceModule.hookAllMethodByName(BluetoothDevice.class, "getAddress", xc_mac_methodHook);


        HookDeviceModule.hookAllMethodByName(BluetoothAdapter.class, "getBondedDevices", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    param.setResult(new HashSet<BluetoothDevice>());
                }
            }
        });

        HookDeviceModule.hookAllMethodByName(BluetoothAdapter.class, "getName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    String bluetoothMac = "";
                    if (MockServices.checkClient()) {
                        bluetoothMac = WXDeviceManager.getInstance().getDeviceInfo("BT_NAME");
                    }
                    param.setResult(bluetoothMac);
                }
            }
        });
    }

    private XC_MethodHook xc_mac_methodHook = new XC_MethodHook() {
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            if (param.getResult() != null) {
                String bluetoothMac = "";
                if (MockServices.checkClient()) {
                    bluetoothMac = WXDeviceManager.getInstance().getDeviceInfo("BTMAC");
                }
                param.setResult(bluetoothMac);
            }
        }
    };

    private void hookUserDer() {
        HookDeviceModule.hookAllMethodByName(UsbDevice.class, "getDeviceId", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    param.setResult(0);
                }
            }
        });
        HookDeviceModule.hookAllMethodByName(UsbDevice.class, "getDeviceName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    param.setResult("");
                }
            }
        });

        HookDeviceModule.hookAllMethodByName(UsbDevice.class, "getSerialNumber", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    param.setResult("");
                }
            }
        });
    }

    private void hookInputDevice() {
        HookDeviceModule.hookAllMethodByName(InputDevice.class, "getName", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    param.setResult("");
                }
            }
        });
    }

    private void hookNetworkInfoName() {

        HookDeviceModule.hookAllMethodByName(NetworkInfo.class, "getExtraInfo", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.getResult() != null) {
                    if (!MockServices.checkClient()) {
                        return;
                    }
                    String ssid = WXDeviceManager.getInstance().getDeviceInfo("SSID");
                    param.setResult(ssid);
                }
            }
        });
    }

    public void testServices() {
        HookDeviceModule.hookAllMethodByName(Activity.class, "getSystemService", new ServiceXcHook("getSystemService"));
        Class contextImpl = null;
        try {
            contextImpl = Class.forName("android.app.ContextImpl");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            LogManager.getInstance().logHookDeviceError("testServices can not find android.app.ContextImpl" + e.getMessage());
        }
        if (null != contextImpl) {
            HookDeviceModule.hookAllMethodByName(contextImpl, "getPackageManager", new ServiceXcHook("getPackageManager"));
            HookDeviceModule.hookAllMethodByName(contextImpl, "getSystemService", new ServiceXcHook("getSystemService"));
        }
    }

    private class ServiceXcHook extends XC_MethodHook {

        private String name;

        public ServiceXcHook(String name) {
            super();
            this.name = name;
        }

        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            if ("getSystemService".equals(name)) {
                if (param.args.length > 0 && param.args[0] instanceof String && param.getResult() != null) {
                    String name = (String) param.args[0];
                    Object instance = param.getResult();
//                    LogManager.getInstance().logHookDeviceInfo("ServiceXcHook afterHookedMethod name = " + name + " instance name = " + instance.getClass().getName());
                    contextGetSystemServices(name, instance.getClass().getName(), param);
                }
            } else if ("getPackageManager".equals(name)) {
                if (param.getResult() != null) {
                    contextGetSystemServices("PackageManager", param.getResult().getClass().getName(), param);
                }
            }
        }
    }

    private void contextGetSystemServices(String name, String className, XC_MethodHook.MethodHookParam param) {

        try {
            if (MockServices.checkClient()) {
                boolean no = MockServices.getClient().canUseService(name, className);
                if (!no) {
                    param.setResult(null);
                }
            } else {
                LogManager.getInstance().logHookDeviceError("contextGetSystemServices client error");
            }
        } catch (RemoteException e) {
            e.printStackTrace();
            LogManager.getInstance().logHookDeviceError("contextGetSystemServices client RemoteException " + e.getMessage());
        }
    }


    //-----Method----


    private boolean hookIntFiled(Class cls, Object object, String fieldName, String serviceKey) {
//        int value = 0;
//        try {
//            if (MockServices.checkClient()) {
//                value = WXDeviceManager.getInstance().getDeviceInfoInt(serviceKey);
//            }
//        } catch (RemoteException e) {
//            e.printStackTrace();
//            LogManager.getInstance().logHookDeviceError("hookFiled error " + e.getMessage());
//        }
//        LogManager.getInstance().logInfo("hookIntFiled value = " + value);

        String strValue = WXDeviceManager.getInstance().getDeviceInfo(serviceKey);
        int intValue = 0;
        try {
            intValue = Integer.parseInt(strValue);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            LogManager.getInstance().logError("");
        }
        LogManager.getInstance().logInfo("key = " + serviceKey + " value " + strValue);
        return setHookField(cls, object, fieldName, intValue);
    }

    private boolean hookFiled(Class cls, Object object, String fieldName, String serviceKey) {
//        String value = null;
//        try {
//            if (MockServices.checkClient()) {
//                value = WXDeviceManager.getInstance().getDeviceInfo(serviceKey);
//            }
//        } catch (RemoteException e) {
//            e.printStackTrace();
//            LogManager.getInstance().logHookDeviceError("hookFiled error " + e.getMessage());
//        }
//
//        if (TextUtils.isEmpty(value)) {
//            LogManager.getInstance().logHookDeviceError("hookFiled value " + value);
//            return false;
//        }
//        LogManager.getInstance().logInfo("key = " + serviceKey + " value " + WXDeviceManager.getInstance().getDeviceInfo(serviceKey));
        return setHookField(cls, object, fieldName, WXDeviceManager.getInstance().getDeviceInfo(serviceKey));
    }

    private boolean setHookField(Class cls, Object object, String fieldName, Object value) {
        try {
            Field serial = cls.getField(fieldName);
            serial.setAccessible(true);
            serial.set(object, value);
            return true;
        } catch (Throwable ex) {
            LogManager.getInstance().logHookDeviceError("hookFiled error " + ex.getMessage());
        }
        return false;
    }


    public boolean hookSystemProperties() {
        try {
            Class sp = Class.forName("android.os.SystemProperties");
            HookDeviceModule.hookAllMethodByName(sp, "get", new SystemTestHook());
            HookDeviceModule.hookAllMethodByName(sp, "getInt", new SystemTestHook());
            HookDeviceModule.hookAllMethodByName(sp, "getLong", new SystemTestHook());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }


    private class SystemTestHook extends XC_MethodHook {
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            Object o = param.getResult();
            if (null != o && o instanceof String) {
                if ("ro.debug_level".equals(param.args[0].toString())) {
                    LogManager.getInstance().logError("SystemTestHook can not hook " + param.args[0]);
                } else {
                    param.setResult("");
                }
            } else if (o instanceof Integer) {
                param.setResult(0);
            } else if (o instanceof Long) {
                param.setResult(0l);
            }
            super.afterHookedMethod(param);
        }
    }


    public boolean hookSystemPropertiesLenovo() {
        try {
            Class sp = Class.forName("android.os.SystemProperties");
            HookDeviceModule.hookAllMethodByName(sp, "get", new SystemPropertiesHook(""));
            HookDeviceModule.hookAllMethodByName(sp, "getInt", new SystemPropertiesHook(0));
            HookDeviceModule.hookAllMethodByName(sp, "getLong", new SystemPropertiesHook(0l));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    private class SystemPropertiesHook extends XC_MethodHook {
        private Object mResult = null;

        public SystemPropertiesHook(Object mResult) {
            this.mResult = mResult;
        }

        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            try {
                param.setResult(mResult);

                StringBuilder sb = new StringBuilder();
                if (null != param.args)
                    for (Object o : param.args) {
                        if (null == o) {
                            sb.append(" null ");
                        } else {
                            sb.append(o.toString());
                        }
                    }
                LogManager.getInstance().logError("SystemPropertiesHook afterHookedMethod " + " param.method = " + param.method.getName() + " arg = " + sb.toString());
            } catch (Exception e) {
                e.printStackTrace();
                LogManager.getInstance().logHookDeviceInfo("SystemPropertiesHook afterHookedMethod error " + e.getMessage());
            }
        }
    }


    private static Method getMethod = null;

    public static String getString(final String key, final String def) {
        try {
            if (getMethod == null) {
                getMethod = Class.forName("android.os.SystemProperties").getMethod("get", String.class);
            }

            return getMethod.invoke(null, key).toString();
        } catch (Exception e) {
            Log.e(TAG, "Platform error: " + e.toString());
            return def;
        }
    }

    private static Method getLongMethod = null;
    private static Method getBooleanMethod = null;

    public static long getLong(final String key, final long def) {
        try {
            if (getLongMethod == null) {
                getLongMethod = Class.forName("android.os.SystemProperties")
                        .getMethod("getLong", String.class, long.class);
            }

            return ((Long) getLongMethod.invoke(null, key, def)).longValue();
        } catch (Exception e) {
            Log.e(TAG, "Platform error: " + e.toString());
            return def;
        }
    }

    public static boolean getBoolean(final String key, final boolean def) {
        try {
            if (getBooleanMethod == null) {
                getBooleanMethod = Class.forName("android.os.SystemProperties")
                        .getMethod("getBoolean", String.class, boolean.class);
            }

            //Log.i(TAG,"getBoolean:"+"key:"+key+" def:"+def);
            //Log.i(TAG,"getBoolean:"+getBooleanMethod.invoke(null, key, def));

            return (Boolean) getBooleanMethod.invoke(null, key, def);
        } catch (Exception e) {
            Log.e(TAG, "Platform error: " + e.toString());
            return def;
        }
    }


    private void hookDisplay() {
        HookDeviceModule.hookAllMethodByName(WindowManager.class, "getDefaultDisplay", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
            }
        });
    }


}
